/*
 * ManagerResource.h
 *
 *  Created on:
 *      Author: Andrey Efimov
 *      Copyright: LGPL
 */

#include "ManagerResource.h"
#include "UnitFabric.h"
#include <iostream>
#include "../Constants.h"
#include "../Visualisator/CVisualisator.h"
#include "../Visualisator/GameObject.h"

CResourceLoader* ResourceOwner::ResourceLoader = NULL;
CResourceWarehouse* ResourceOwner::ResourceWarehouse = NULL;

const string CResourceLoader::GUI_SUOBJECT_NAME = "GUI";
const string CResourceLoader::GUI_SHADER_PROGRAM = "Render2DObjest";

CResourceLoader*
ResourceOwner::GetResourceLoader()
{
	if ( NULL == ResourceLoader )
	{
		ResourceLoader = new CResourceLoader();
	}
	return ResourceLoader;
}

void
ResourceOwner::DeleteResourceLoader()
{
	if ( NULL != ResourceLoader )
	{
		delete ResourceLoader;
		ResourceLoader = NULL;
	}
}

CResourceWarehouse*
ResourceOwner::GetResourceWarehouse()
{
	if ( NULL == ResourceWarehouse )
	{
		ResourceWarehouse = new CResourceWarehouse();
	}
	return ResourceWarehouse;
}

void
ResourceOwner::DeleteResourceWarehouse()
{
	if ( NULL != ResourceWarehouse )
	{
		delete ResourceWarehouse;
		ResourceWarehouse = NULL;
	}
}

//------------------------------------------------------------------------------------

vector<IShaderParamParser*> *CResourceLoader::shaderParamParsers = NULL;

CResourceLoader::CResourceLoader()
{
	if ( NULL == shaderParamParsers )
	{
		shaderParamParsers = new vector<IShaderParamParser*>();
		shaderParamParsers->push_back( new CParseCommonInputValues() );
		shaderParamParsers->push_back( new CParseNativeInputTypes() );
		shaderParamParsers->push_back( new CParseSinCallbac() );
	}
}
//----�������� ������--------------------------
bool
CResourceLoader::GetShablon( const char* FileShablon, vector<SAllShablon> &AllShablonResult )
{
	SAllShablon AllShablonTemp;
	SOneShablon OneShablonTemp;
	char buffer[1024];
	STextTexture TextTexture;
	bool Contain = false;//TODO Flag proverki zapolneniy AllShablonTemp, dly nevneseniy pustogo soderjimogo

	FILE *file = fopen( FileShablon, "rt" );
	while ( fgets( buffer, 1023, file ) )
	{
		buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';

		//----CLabel--------------------------------------
		if ((strcmp(buffer,"CLabel") == 0))
		{
			if (Contain != false)
				AllShablonResult.push_back(AllShablonTemp);
			Contain = true;
			AllShablonTemp.Type = TYPE_LABEL;
			AllShablonTemp.OneShablon.clear();
			fgets( buffer, 1023, file );
			buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';
		}
		//----CEdit--------------------------------------
		if ((strcmp(buffer,"CEdit") == 0))
		{
			if (Contain != false)
				AllShablonResult.push_back(AllShablonTemp);
			Contain = true;
			AllShablonTemp.Type = TYPE_EDIT;
			AllShablonTemp.OneShablon.clear();
			fgets( buffer, 1023, file );
			buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';
		}
		//----CMemo--------------------------------------
		if ((strcmp(buffer,"CMemo") == 0))
		{
			if (Contain != false)
				AllShablonResult.push_back(AllShablonTemp);
			Contain = true;
			AllShablonTemp.Type = TYPE_MEMO;
			AllShablonTemp.OneShablon.clear();
			fgets( buffer, 1023, file );
			buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';
		}
		//----CListBox--------------------------------------
		if ((strcmp(buffer,"CListBox") == 0))
		{
			if (Contain != false)
				AllShablonResult.push_back(AllShablonTemp);
			Contain = true;
			AllShablonTemp.Type = TYPE_LISTBOX;
			AllShablonTemp.OneShablon.clear();
			fgets( buffer, 1023, file );
			buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';
		}
		//----CComboBox--------------------------------------
		if ((strcmp(buffer,"CComboBox") == 0))
		{
			if (Contain != false)
				AllShablonResult.push_back(AllShablonTemp);
			Contain = true;
			AllShablonTemp.Type = TYPE_COMBOBOX;
			AllShablonTemp.OneShablon.clear();
			fgets( buffer, 1023, file );
			buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';
		}
		//----CButton--------------------------------------
		if ((strcmp(buffer,"CButton") == 0))
		{
			if (Contain != false)
				AllShablonResult.push_back(AllShablonTemp);
			Contain = true;
			AllShablonTemp.Type = TYPE_BUTTON;
			AllShablonTemp.OneShablon.clear();
			fgets( buffer, 1023, file );
			buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';
		}
		//----CCheckBox--------------------------------------
		if ((strcmp(buffer,"CCheckBox") == 0))
		{
			if (Contain != false)
				AllShablonResult.push_back(AllShablonTemp);
			Contain = true;
			AllShablonTemp.Type = TYPE_CHECKBOX;
			AllShablonTemp.OneShablon.clear();
			fgets( buffer, 1023, file );
			buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';
		}
		//----CRadioButton--------------------------------------
		if ((strcmp(buffer,"CRadioButton") == 0))
		{
			if (Contain != false)
				AllShablonResult.push_back(AllShablonTemp);
			Contain = true;
			AllShablonTemp.Type = TYPE_RADIOBUTTON;
			AllShablonTemp.OneShablon.clear();
			fgets( buffer, 1023, file );
			buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';
		}
		//----CProgressBar--------------------------------------
		if ((strcmp(buffer,"CProgressBar") == 0))
		{
			if (Contain != false)
				AllShablonResult.push_back(AllShablonTemp);
			Contain = true;
			AllShablonTemp.Type = TYPE_PROGRESSBAR;
			AllShablonTemp.OneShablon.clear();
			fgets( buffer, 1023, file );
			buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';
		}
		//----CTrackBar--------------------------------------
		if ((strcmp(buffer,"CTrackBar") == 0))
		{
			if (Contain != false)
				AllShablonResult.push_back(AllShablonTemp);
			Contain = true;
			AllShablonTemp.Type = TYPE_TRACKBAR;
			AllShablonTemp.OneShablon.clear();
			fgets( buffer, 1023, file );
			buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';
		}
		//----CPanel--------------------------------------
		if ((strcmp(buffer,"CPanel") == 0))
		{
			if (Contain != false)
				AllShablonResult.push_back(AllShablonTemp);
			Contain = true;
			AllShablonTemp.Type = TYPE_PANEL;
			AllShablonTemp.OneShablon.clear();
			fgets( buffer, 1023, file );
			buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';
		}
		//----CInventar--------------------------------------
		if ((strcmp(buffer,"CInventar") == 0))
		{
			if (Contain != false)
				AllShablonResult.push_back(AllShablonTemp);
			Contain = true;
			AllShablonTemp.Type = TYPE_INVENTAR;
			AllShablonTemp.OneShablon.clear();
			fgets( buffer, 1023, file );
			buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';
		}
		if(strcmp(buffer,"end") == 0)
		{
			if (Contain != false)
				AllShablonResult.push_back(AllShablonTemp);
			break;
		}
		//TODO Sdelat proverku sscanf na poluchenie nujnoi informatii, otsekay musor
		sscanf( buffer, "%s %s %s %s %s", TextTexture.Texture1,
				TextTexture.Texture2, TextTexture.Texture3,
				TextTexture.Texture4, TextTexture.Texture5);
		UINT x, y;
		OneShablonTemp.Texture1 = SetUpTexture2D( std::string(TextTexture.Texture1), x, y );
		OneShablonTemp.Texture2 = SetUpTexture2D( std::string(TextTexture.Texture2), x, y );
		OneShablonTemp.Texture3 = SetUpTexture2D( std::string(TextTexture.Texture3), x, y );
		OneShablonTemp.Texture4 = SetUpTexture2D( std::string(TextTexture.Texture4), x, y );
		OneShablonTemp.Texture5 = SetUpTexture2D( std::string(TextTexture.Texture5), x, y );
		OneShablonTemp.Size.x = float(x)/float(WINDOW_WIDTH);
		OneShablonTemp.Size.y = float(y)/float(WINDOW_HEIGHT);
		AllShablonTemp.OneShablon.push_back(OneShablonTemp);
	}
	fclose(file);
	file = NULL;

	return true;
}

bool
CResourceLoader::GetMapGUI( const char* FileControl, vector<SMapGUI> &MapGUIResult )
{
	SMapGUI MapGUITemp;
	SOneMapGUI OneMapGUITemp;
	char buffer[1024];
	char NameTemp[1024];
	char ParentTemp[1024];
	bool Contain = false;//TODO Flag proverki zapolneniy MapGUITemp, dly nevneseniy pustogo soderjimogo

	FILE *file = fopen( FileControl, "rt" );
	while ( fgets( buffer, 1023, file ) )
	{
		buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';

		//----CLabel--------------------------------------
		if ((strcmp(buffer,"CLabel") == 0))
		{
			if (Contain != false)
				MapGUIResult.push_back(MapGUITemp);
			Contain = true;
			MapGUITemp.Type = TYPE_LABEL;
			MapGUITemp.OneMapGUI.clear();
			fgets( buffer, 1023, file );
			buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';
		}
		//----CEdit--------------------------------------
		if ((strcmp(buffer,"CEdit") == 0))
		{
			if (Contain != false)
				MapGUIResult.push_back(MapGUITemp);
			Contain = true;
			MapGUITemp.Type = TYPE_EDIT;
			MapGUITemp.OneMapGUI.clear();
			fgets( buffer, 1023, file );
			buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';
		}
		//----CMemo--------------------------------------
		if ((strcmp(buffer,"CMemo") == 0))
		{
			if (Contain != false)
				MapGUIResult.push_back(MapGUITemp);
			Contain = true;
			MapGUITemp.Type = TYPE_MEMO;
			MapGUITemp.OneMapGUI.clear();
			fgets( buffer, 1023, file );
			buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';
		}
		//----CListBox--------------------------------------
		if ((strcmp(buffer,"CListBox") == 0))
		{
			if (Contain != false)
				MapGUIResult.push_back(MapGUITemp);
			Contain = true;
			MapGUITemp.Type = TYPE_LISTBOX;
			MapGUITemp.OneMapGUI.clear();
			fgets( buffer, 1023, file );
			buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';
		}
		//----CComboBox--------------------------------------
		if ((strcmp(buffer,"CComboBox") == 0))
		{
			if (Contain != false)
				MapGUIResult.push_back(MapGUITemp);
			Contain = true;
			MapGUITemp.Type = TYPE_COMBOBOX;
			MapGUITemp.OneMapGUI.clear();
			fgets( buffer, 1023, file );
			buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';
		}
		//----CButton--------------------------------------
		if ((strcmp(buffer,"CButton") == 0))
		{
			if (Contain != false)
				MapGUIResult.push_back(MapGUITemp);
			Contain = true;
			MapGUITemp.Type = TYPE_BUTTON;
			MapGUITemp.OneMapGUI.clear();
			fgets( buffer, 1023, file );
			buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';
		}
		//----CCheckBox--------------------------------------
		if ((strcmp(buffer,"CCheckBox") == 0))
		{
			if (Contain != false)
				MapGUIResult.push_back(MapGUITemp);
			Contain = true;
			MapGUITemp.Type = TYPE_CHECKBOX;
			MapGUITemp.OneMapGUI.clear();
			fgets( buffer, 1023, file );
			buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';
		}
		//----CRadioButton--------------------------------------
		if ((strcmp(buffer,"CRadioButton") == 0))
		{
			if (Contain != false)
				MapGUIResult.push_back(MapGUITemp);
			Contain = true;
			MapGUITemp.Type = TYPE_RADIOBUTTON;
			MapGUITemp.OneMapGUI.clear();
			fgets( buffer, 1023, file );
			buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';
		}
		//----CProgressBar--------------------------------------
		if ((strcmp(buffer,"CProgressBar") == 0))
		{
			if (Contain != false)
				MapGUIResult.push_back(MapGUITemp);
			Contain = true;
			MapGUITemp.Type = TYPE_PROGRESSBAR;
			MapGUITemp.OneMapGUI.clear();
			fgets( buffer, 1023, file );
			buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';
		}
		//----CTrackBar--------------------------------------
		if ((strcmp(buffer,"CTrackBar") == 0))
		{
			if (Contain != false)
				MapGUIResult.push_back(MapGUITemp);
			Contain = true;
			MapGUITemp.Type = TYPE_TRACKBAR;
			MapGUITemp.OneMapGUI.clear();
			fgets( buffer, 1023, file );
			buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';
		}
		//----CPanel--------------------------------------
		if ((strcmp(buffer,"CPanel") == 0))
		{
			if (Contain != false)
				MapGUIResult.push_back(MapGUITemp);
			Contain = true;
			MapGUITemp.Type = TYPE_PANEL;
			MapGUITemp.OneMapGUI.clear();
			fgets( buffer, 1023, file );
			buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';
		}
		//----CInventar--------------------------------------
		if ((strcmp(buffer,"CInventar") == 0))
		{
			if (Contain != false)
				MapGUIResult.push_back(MapGUITemp);
			Contain = true;
			MapGUITemp.Type = TYPE_INVENTAR;
			MapGUITemp.OneMapGUI.clear();
			fgets( buffer, 1023, file );
			buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';
		}
		if(strcmp(buffer,"end") == 0)
		{
			if (Contain != false)
				MapGUIResult.push_back(MapGUITemp);
			break;
		}
		//TODO Sdelat proverku sscanf na poluchenie nujnoi informatii, otsekay musor
		sscanf( buffer, "%s %s %d %f %f", NameTemp,
				ParentTemp, &OneMapGUITemp.TipShablon,
				&OneMapGUITemp.Position.x, &OneMapGUITemp.Position.y);
		OneMapGUITemp.Name = NameTemp;
		OneMapGUITemp.Parent = ParentTemp;
		if ( OneMapGUITemp.Parent == "NULL" )
			OneMapGUITemp.Parent = "";

		MapGUITemp.OneMapGUI.push_back(OneMapGUITemp);
	}
	fclose(file);
	file = NULL;

	return true;
}

/**
 *
 * Function loads texture with name FileName and returns it's dimensions through and Height
 *
 * The function has problems with .dds files with mip-levels,
 * in this case use overloaded version SetUpTexture2D( const std::string FileName )
 */
GLuint
CResourceLoader::SetUpTexture2D( const std::string FileName, UINT &Width, UINT &Height ) const
{
	GLuint Result = 0;
	if ( !FileName.empty() )
	{
		gli::texture2D Texture = gli::gtx::loader::load( FileName );
		if( !Texture.empty() )
		{
			gli::gtx::gl_texture2d::detail::texture_desc TextureDesc =
					gli::gtx::gl_texture2d::detail::gli2ogl_cast( Texture.format() );

			GLint Alignment = 0;
			glGetIntegerv(GL_UNPACK_ALIGNMENT, &Alignment);

			glPixelStorei(GL_UNPACK_ALIGNMENT, 4);

			GLuint Name = 0;
			glGenTextures(1, &Name);
			glBindTexture(GL_TEXTURE_2D, Name);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, Texture.levels() > 1 ? GL_NEAREST_MIPMAP_NEAREST : GL_LINEAR);
			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

			Width = GLsizei( Texture[0].dimensions().x );
			Height = GLsizei( Texture[0].dimensions().y );


			if( gli::size( Texture, gli::BIT_PER_PIXEL ) ==  ( gli::size( Texture, gli::BLOCK_SIZE ) << 3 ))
			{
				for(gli::texture2D::level_type Level = 0; Level < Texture.levels(); ++Level)
				{
					glTexImage2D( GL_TEXTURE_2D,
								  GLint(Level),
							      TextureDesc.InternalFormat,
							      Width,
							      Height,
							      0,
							      TextureDesc.ExternalFormatRev,
							      TextureDesc.Type,
							      Texture[Level].data() );
				}
			}
			else
			{
				for( gli::texture2D::level_type Level = 0; Level < Texture.levels(); ++Level )
				{
					glCompressedTexImage2D( GL_TEXTURE_2D,
							                GLint(Level),
											TextureDesc.InternalFormat,
											Width,
											Height,
											0,
											GLsizei(Texture[Level].capacity()),
											Texture[Level].data() );
				}
			}

			glBindTexture( GL_TEXTURE_2D, 0 );
			glPixelStorei( GL_UNPACK_ALIGNMENT, Alignment );
		}
		Result = gli::createTexture2D( FileName );
	}
	return Result;
}

GLuint
CResourceLoader::SetUpTexture2D( const std::string FileName ) const
{
	return gli::createTexture2D( FileName );
}

/*
 * The function loads an array of textures is used for multitexturing in GLSL
 */
GLuint
CResourceLoader::SetUpTexture2D( const std::vector<std::string> FileNames ) const
{
	GLuint         Result = 0;
	bool           FirstTexture = true;
	gli::gtx::gl_texture2d::detail::texture_desc TextureDesc;
	GLint          Alignment = 0;
	UINT           ImageWidth, ImageHeight;
	BYTE          *CommonPixelsArray = NULL;
	int            BitPerPixel, BlockSize;
	UINT           Texture3DLevelsCounter = 0;
	GLenum         OneImageArraySize = 0;

	if ( !FileNames.empty() )
	{

		for ( std::vector<std::string>::const_iterator it = FileNames.begin(), end = FileNames.end();
			  it != end; ++it )
		{
			std::string FileName = *it.base();
			gli::texture2D Texture = gli::gtx::loader::load( FileName );
			if( Texture.empty() )
			{
				fprintf( stderr, "Error texture %s not found..\n.", FileName.c_str() );
				break;
			}

			gli::gtx::gl_texture2d::detail::texture_desc CurrentDescriptor =
					gli::gtx::gl_texture2d::detail::gli2ogl_cast( Texture.format() );
			if ( FirstTexture )
			{
				TextureDesc = CurrentDescriptor;

				ImageWidth = GLsizei( Texture[0].dimensions().x );
				ImageHeight = GLsizei( Texture[0].dimensions().y );
				BitPerPixel = (int)gli::size( Texture, gli::BIT_PER_PIXEL );
				BlockSize = (int)gli::size( Texture, gli::BLOCK_SIZE );
				OneImageArraySize = (BitPerPixel / 8) * ImageWidth * ImageHeight;

				CommonPixelsArray = new BYTE[OneImageArraySize * FileNames.size()];
			}
			else if ( ( GLsizei( Texture[0].dimensions().x ) != ImageWidth ) ||
					  ( GLsizei( Texture[0].dimensions().y ) != ImageHeight ) ||
					  ( TextureDesc.InternalFormat != CurrentDescriptor.InternalFormat ) )
			{
				fprintf( stderr, "Error texture %s has incompatible format or dimension... \n", FileName.c_str() );
				break;
			}

			memcpy( CommonPixelsArray + OneImageArraySize * Texture3DLevelsCounter, Texture[0].data(), OneImageArraySize );
			Texture3DLevelsCounter++;
		}

		if ( CommonPixelsArray != NULL )
		{
			glGetIntegerv(GL_UNPACK_ALIGNMENT, &Alignment);
			glPixelStorei(GL_UNPACK_ALIGNMENT, 4);

			glGenTextures( 1, &Result );
			glBindTexture( GL_TEXTURE_2D_ARRAY, Result );

			glTexParameteri(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
			glTexParameteri(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
			glTexParameteri(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_WRAP_S,GL_REPEAT); //What to do when texture coordinate is not in [0..1]
			glTexParameteri(GL_TEXTURE_2D_ARRAY,GL_TEXTURE_WRAP_T,GL_REPEAT);

			if( BitPerPixel == ( BlockSize << 3 ) )
			{
				glTexImage3D( GL_TEXTURE_2D_ARRAY,
							  0, //TODO Add support for loading miplevels
							  TextureDesc.InternalFormat,
							  ImageWidth,
							  ImageHeight,
							  Texture3DLevelsCounter,
							  0,
							  TextureDesc.ExternalFormatRev,
							  TextureDesc.Type,
							  CommonPixelsArray );

			}
			else
			{
				glCompressedTexImage3D( GL_TEXTURE_2D_ARRAY,
							            0, //TODO Add support for loading miplevels
							            TextureDesc.InternalFormatCompressed,
										ImageWidth,
										ImageHeight,
										Texture3DLevelsCounter,
										0,
										OneImageArraySize * Texture3DLevelsCounter,
										CommonPixelsArray );
			}

			glBindTexture( GL_TEXTURE_2D_ARRAY, 0 );
			glPixelStorei( GL_UNPACK_ALIGNMENT, Alignment );
		}

		delete [] CommonPixelsArray;
	}

	return Result;
}

/*
 * Loads all units described in configuration file including their
 * materials, textures, models and animations
 */
bool
CResourceLoader::LoadGameObjectsFromFile( const std::string FileName )
{
	bool Result = false;
	char buffer[FILE_STRING_MAX_LENGHT];
	FILE *file = fopen( FileName.c_str(), "rt" );
	CResourceWarehouse* Warehouse = ResourceOwner::GetResourceWarehouse();
	CVisualisator* Visualisator   = CVisualisator::GetVisualisator();
	boost::unordered_map< string, GAME_SUBOBJECT_INSTANCE* > SubobjectsByNames;
	boost::unordered_map< string, SubobjectConstructionInfo* > SubobjectConstructionInfoByNames;

	Result = ( file != NULL ) && Visualisator->IsInitialized();
	if ( Result )
	{
		while ( fgets( buffer, FILE_STRING_MAX_LENGHT, file ) )
		{
			buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';
			if ( strcmp( buffer, "Texture" ) == 0 )
			{
				vector<string> TextureNamesFromFile;
				vector< pair<GLuint,GLenum> > TextureIDsFromFile;

				ReadTexturesFromFile( file, TextureNamesFromFile, TextureIDsFromFile );
				assert( TextureNamesFromFile.size() == TextureIDsFromFile.size() );

				//Add textures to warehouse
				for( UINT i = 0; i < TextureNamesFromFile.size(); i++ )
				{
					TEXTURE_DESCRIPTOR* NewDescriptor = Warehouse->GetTextureDescriptorByName( TextureNamesFromFile[i] );

					// If texture with such name isn't loaded yet
					if ( NULL == NewDescriptor )
					{
						NewDescriptor = new TEXTURE_DESCRIPTOR( TextureNamesFromFile[i],
							 	 	 	 	 	 	 	 		0,
							 	 	 	 	 	 	 	 		TextureIDsFromFile[i].first,
							 	 	 	 	 	 	 	 		TextureIDsFromFile[i].second );
						Warehouse->AddTextureDescriptor( NewDescriptor );
					}
				}
			}
			else if ( strcmp( buffer, "Model" ) == 0 )
			{
				buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';
				fgets( buffer, FILE_STRING_MAX_LENGHT, file );
				while ( strcmp( buffer, "end" ) != 0 )
				{
					string ModelName;
					float DimensionSphereRadius;
					glm::vec3 DimensionSphereCenter, DimensionCorner;
					RENDER_MODEL_INFO *LoadedModelRenderInfo = LoadModelFromFileAndRegister( buffer,
																							 ModelName,
																							 DimensionSphereRadius,
																							 DimensionSphereCenter,
																							 DimensionCorner );
					//Create dummy subobject
					GAME_SUBOBJECT_INSTANCE* NewSubobject = new GAME_SUBOBJECT_INSTANCE( LoadedModelRenderInfo,
																						 glm::mat4( 1.0 ),
																						 0 );


					NewSubobject->dimensionCorner = DimensionCorner - DimensionSphereCenter;
					NewSubobject->correctionMatrix = glm::inverse( glm::translate( glm::mat4(1.0), DimensionSphereCenter ) );

					NewSubobject->sphereRadius = DimensionSphereRadius;

					SubobjectsByNames.emplace( ModelName, NewSubobject );

					fgets( buffer, FILE_STRING_MAX_LENGHT, file );
					buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';
				}
			}
			else if ( strcmp( buffer, "Subobject" ) == 0 )
			{
				SubobjectConstructionInfo* NewSubobject = LoadSubobjectFromFile( file );
				SubobjectConstructionInfoByNames.emplace( NewSubobject->name, NewSubobject );
			}
			else if ( strcmp( buffer, "UnitType" ) == 0 )
			{
				UnitTypeInfo* NewUnitType = LoadUnitTypesFromFile( file );

				if ( NewUnitType != NULL )
				{
					int SubobjectsInUnit = NewUnitType->subobjects.size();

					GAME_OBJECT* UnitGameObject = new GAME_OBJECT();
					UnitGameObject->models.reserve( SubobjectsInUnit );

					glm::vec3 DimensionMinCorner, DimensionMaxCorner;
					for ( int i = 0; i < SubobjectsInUnit; i++ )
					{
						// Take textures and animations for this unit
						boost::unordered_map< string, SubobjectConstructionInfo* >::iterator SubobjectConstructionIterator =
										SubobjectConstructionInfoByNames.find( NewUnitType->subobjects[i].subobjectName );
						assert( SubobjectConstructionIterator != SubobjectConstructionInfoByNames.end() );

						SubobjectConstructionInfo* CustomInfo = SubobjectConstructionIterator->second;

						boost::unordered_map< string, GAME_SUBOBJECT_INSTANCE* >::iterator SubobjectsItertor =
																			SubobjectsByNames.find( CustomInfo->modelName );
						assert( SubobjectsItertor != SubobjectsByNames.end() );

						GAME_SUBOBJECT_INSTANCE *Prototype = SubobjectsItertor->second;
						GAME_SUBOBJECT_INSTANCE *Subobject = new GAME_SUBOBJECT_INSTANCE( *Prototype );
						Subobject->SetShaderProgram( NewUnitType->subobjects[i].shaderProgram );

						SUBOBJECT_WITH_CONTROLLER *SubobjectWithController = new SUBOBJECT_WITH_CONTROLLER( Subobject, NULL );
						SetupSubobjectParameters( *SubobjectWithController, *CustomInfo );

						glm::mat4 ScaleMatrix = glm::scale( ( 0 != Subobject->dimensionCorner.x) ?
															( NewUnitType->subobjects[i].dimesionSizes.x /
															  Subobject->dimensionCorner.x ) :
															0.0f,
															( 0 != Subobject->dimensionCorner.y) ?
															( NewUnitType->subobjects[i].dimesionSizes.y /
															  Subobject->dimensionCorner.y ) :
															0.0f,
															( 0 != Subobject->dimensionCorner.z) ?
															( NewUnitType->subobjects[i].dimesionSizes.z /
															  Subobject->dimensionCorner.z ) :
															0.0f );
						glm::mat4 TranslationMatrix = glm::translate( NewUnitType->subobjects[i].relativePosition.x,
																	  NewUnitType->subobjects[i].relativePosition.y,
																	  NewUnitType->subobjects[i].relativePosition.z );
						glm::quat tmpQuat = glm::angleAxis( 1.0f,
															NewUnitType->subobjects[i].relativeOrientation.x,
															NewUnitType->subobjects[i].relativeOrientation.y,
															NewUnitType->subobjects[i].relativeOrientation.z );

						glm::mat4 TransformationMatrix = TranslationMatrix * glm::toMat4( tmpQuat ) * ScaleMatrix;

						glm::vec4 CurrentMinCorner = glm::vec4( Subobject->dimensionCorner, 1.0 ) * glm::vec4( -1.0f );
						CurrentMinCorner = CurrentMinCorner * TransformationMatrix;
						glm::vec4 CurrentMaxCorner = glm::vec4( Subobject->dimensionCorner, 1.0 ) * TransformationMatrix;

						Subobject->name = NewUnitType->subobjects[i].subobjectName;
						UnitGameObject->models.push_back( SubobjectWithController );
						Subobject->position = TransformationMatrix;

						//Correct values of temp corners
						if ( CurrentMinCorner.x > CurrentMaxCorner.x )
						{
							float Accumulator = CurrentMaxCorner.x;
							CurrentMaxCorner.x = CurrentMinCorner.x;
							CurrentMinCorner.x = Accumulator;
						}
						if ( CurrentMinCorner.y > CurrentMaxCorner.y )
						{
							float Accumulator = CurrentMaxCorner.y;
							CurrentMaxCorner.y = CurrentMinCorner.y;
							CurrentMinCorner.y = Accumulator;
						}
						if ( CurrentMinCorner.z > CurrentMaxCorner.z )
						{
							float Accumulator = CurrentMaxCorner.z;
							CurrentMaxCorner.z = CurrentMinCorner.z;
							CurrentMinCorner.z = Accumulator;
						}
						Subobject->dimensionCorner = glm::vec3( CurrentMaxCorner.x,
																CurrentMaxCorner.y,
																CurrentMaxCorner.z );

						if ( 0 == i )
						{
							//initialize dimensions
							DimensionMinCorner = glm::vec3( CurrentMinCorner.x,
															CurrentMinCorner.y,
															CurrentMinCorner.z );
							DimensionMaxCorner = glm::vec3( CurrentMaxCorner.x,
															CurrentMaxCorner.y,
															CurrentMaxCorner.z );
						}
						//Update dimension
						else
						{
							if ( DimensionMinCorner.x > CurrentMinCorner.x )
							{
								DimensionMinCorner.x = CurrentMinCorner.x;
							}
							if ( DimensionMinCorner.y > CurrentMinCorner.y )
							{
								DimensionMinCorner.y = CurrentMinCorner.y;
							}
							if ( DimensionMinCorner.z > CurrentMinCorner.z )
							{
								DimensionMinCorner.z = CurrentMinCorner.z;
							}

							if ( DimensionMaxCorner.x < CurrentMaxCorner.x )
							{
								DimensionMaxCorner.x = CurrentMaxCorner.x;
							}
							if ( DimensionMaxCorner.y < CurrentMaxCorner.y )
							{
								DimensionMaxCorner.y = CurrentMaxCorner.y;
							}
							if ( DimensionMaxCorner.z < CurrentMaxCorner.z )
							{
								DimensionMaxCorner.z = CurrentMaxCorner.z;
							}
						}
					}

					glm::vec3 tmpTranslationVector = ( DimensionMinCorner + DimensionMaxCorner ) / 2.0f;
					UnitGameObject->correctionMatrix = glm::translate( glm::mat4(1.0),
																	   tmpTranslationVector.x,
																	   tmpTranslationVector.y,
																	   tmpTranslationVector.z );
					UnitGameObject->sphereRadius = glm::length( DimensionMaxCorner - DimensionMinCorner ) / 2.0;
					UnitGameObject->dimensionCorner = DimensionMaxCorner - tmpTranslationVector;

					//Create a game object template
					UnitFabric::AddTemplate( NewUnitType->unitType, NewUnitType->name, UnitGameObject );
				}
			}
		}

		boost::unordered_map< string, GAME_SUBOBJECT_INSTANCE* >::iterator Found =
																SubobjectsByNames.find( GUI_SUOBJECT_NAME );
		if ( SubobjectsByNames.end() == Found )
		{
			fprintf( stderr, "No subobject with name %s is loaded!\n", GUI_SUOBJECT_NAME.c_str() );
			exit(0);
		}

		GAME_SUBOBJECT_INSTANCE *GuiSubobject = Found->second;
		CShaderConstructor *ShaderConstructor = Warehouse->GetShaderProgramConstructor( GUI_SHADER_PROGRAM );
		IShaderProgram *GuiShader = ShaderConstructor->ConstructShader( "" );
		GuiSubobject->textureDescriptors.clear(); // GUI dummy shouldn't contain a texture
		GuiSubobject->SetShaderProgram( GuiShader );
		SetupGuiDummy( GuiSubobject );

		//Register vertex array in OpenGL
		Warehouse->SaveVertexArrayId( Visualisator->SetUpModels( compressedModelsAray ) );
		compressedModelsAray.clear();
	}
	return Result;
}

bool
CResourceLoader::LoadShadersFromFile( const std::string FileName )
{
	bool Result = false;
	char buffer[FILE_STRING_MAX_LENGHT];
	FILE *file = fopen( FileName.c_str(), "rt" );

	CResourceWarehouse* Warehouse = ResourceOwner::GetResourceWarehouse();
	CVisualisator* Visualisator   = CVisualisator::GetVisualisator();

	// Pair 1 - Shader OGL id, 2 - vector of texture names
	boost::unordered_map< string, std::pair<GLuint, std::vector<std::string> > > LoadedShaders;

	Result = ( file != NULL ) && Visualisator->IsInitialized();
	if ( Result )
	{
		while ( fgets( buffer, FILE_STRING_MAX_LENGHT, file ) )
		{
			buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';
			// Start reading a shader
			if ( strcmp( buffer, "Effect" ) == 0 )
			{
				//Read shader program type
				fgets( buffer, FILE_STRING_MAX_LENGHT, file );
				buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';
				std::string ProgramTypeName( buffer );

				fgets( buffer, FILE_STRING_MAX_LENGHT, file );
				buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';
				const std::pair<GLuint, std::vector<std::string> > VertexShader =
											GetShaderName( std::string( buffer ), LoadedShaders );

				fgets( buffer, FILE_STRING_MAX_LENGHT, file );
				buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';
				const std::pair<GLuint, std::vector<std::string> > FragmentShader =
											GetShaderName( std::string( buffer ), LoadedShaders );

				if ( ( 0 == VertexShader.first ) || ( 0 == FragmentShader.first ) )
				{
					Result = false;
					break;
				}

				GLuint ShaderProgram = Visualisator->LinkShaderProgram( VertexShader.first, FragmentShader.first );
				fprintf( stdout, "ShaderProgramm: %u is linked from shaders: %u and %u\n",
						ShaderProgram, VertexShader.first, FragmentShader.first  );
				if ( 0 == ShaderProgram )
				{
					Result = false;
					break;
				}

				//Read shader parameters
				string ShaderParameters;
				while ( fgets( buffer, FILE_STRING_MAX_LENGHT, file ) )
				{
					buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';
					if ( strcmp( buffer, "end" ) == 0 )
					{
						//End of parameters list
						break;
					}
					ShaderParameters += string( buffer );
				}

				std::vector<std::string> UnitedSortedTextures( VertexShader.second );
				UnitedSortedTextures.reserve( UnitedSortedTextures.size() + FragmentShader.second.size() );
				UnitedSortedTextures.insert( UnitedSortedTextures.end(), FragmentShader.second.begin(), FragmentShader.second.end() );

				Warehouse->CreateShaderProgramConstructor( ProgramTypeName,
														   ShaderProgram,
														  *shaderParamParsers,
														   ShaderParameters,
														   UnitedSortedTextures );
			}
		}
	}

	return Result;
}

CGameWorld*
CResourceLoader::LoadGameWorld( const std::string FileName )
{
	CGameWorld* Result = NULL;

	char buffer[FILE_STRING_MAX_LENGHT];
	FILE *file = fopen( FileName.c_str(), "rt" );

	CGameWorld::CPhysicsParam* PhysicsParam = LoadGameWorldPhysics( file );

	CGameWorld::CCameraParam*  CameraParam = LoadGameWorldCamera( file );

	std::vector<CGameWorld::CGameObjectParam* > GameObjects = LoadGameWorldObjects( file );

	fclose(file);

	Result = new CGameWorld( PhysicsParam, CameraParam, GameObjects );

	return Result;
}

void
CResourceLoader::LoadGameLogicConfigFromFile( IGameLogic* GameLogic,
		                                      std::vector<std::string> ConfigFilesList )
{
	for ( std::vector<std::string>::const_iterator
			Begin = ConfigFilesList.begin(), End = ConfigFilesList.end();
			Begin != End;
			Begin++ )
	{
		std::string FileName = *Begin;
		char buffer[FILE_STRING_MAX_LENGHT];

		FILE *File = fopen( FileName.c_str(), "rt" );
		assert( fgets( buffer, FILE_STRING_MAX_LENGHT, File ) );
		buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';

		if ( strcmp( buffer, TO_STRING( GAME_LOGIC_DATA_TABLE_KEYS ) ) )
		{
			LoadKeyboardConfigFromFile( File, GameLogic );
		}
		else
		{
			assert( false );
		}

		fclose(file);
	}
}

void
CResourceLoader::ParseVertex( const char* VertString, STANDART_VERTEX *Target )
{
	int TempNull;
	float TempNullF;

	sscanf( VertString, "%d %f %f %f %f %f %f %f %f %d %d %f", &TempNull,
			&Target->position.x, &Target->position.y, &Target->position.z,
			&Target->normal.x, &Target->normal.y, &Target->normal.z,
			&Target->texture.x, &Target->texture.y,
			&TempNull, &TempNull, &TempNullF);
}

CResourceLoader::SmdModel*
CResourceLoader::CompressBuffersToModel( const STANDART_VERTEX *Vertices, const UINT VertCount )
{
	SmdModel* Result = new SmdModel();
	Result->triangleCount = VertCount / 3;
	Result->indexes = new GLushort[VertCount];

	memset( (void*)( Result->indexes ), 0xFFFF, size_t( sizeof(GLushort) * VertCount ) );

	STANDART_VERTEX CompressedVertices[VertCount];
	GLushort CurrentIndex = 0;

	for ( UINT i = 0; i < VertCount; i++ )
	{
        if ( 0xFFFF == Result->indexes[i] )
        {
        	STANDART_VERTEX tmpVertex = Vertices[i];
        	Result->indexes[i] = CurrentIndex;
        	for ( UINT j = (i+1); j < VertCount; j++ )
        	{
        		if (( Vertices[j].position.x == tmpVertex.position.x ) &&
        			( Vertices[j].position.y == tmpVertex.position.y ) &&
        			( Vertices[j].position.z == tmpVertex.position.z ))
        		{
        			Result->indexes[j] = CurrentIndex;
        		}
        	}
        	CompressedVertices[CurrentIndex] = tmpVertex;
        	CurrentIndex++;
        }
	}

	Result->vertices = new STANDART_VERTEX[CurrentIndex];
	Result->vertexCount = CurrentIndex;

	memcpy( (void*)Result->vertices, (void*)CompressedVertices, sizeof(STANDART_VERTEX) * CurrentIndex );

	return Result;
}

CResourceLoader::SmdModel*
CResourceLoader::LoadSMDModel( const char* FileName )
{
	SmdModel* Result = NULL;
	std::vector<TEXTURE_DESCRIPTOR> Textures;

	char buffer[FILE_STRING_MAX_LENGHT];
	STANDART_VERTEX* TempVertex = NULL;
	std::string      TextureNameCash = "";
	UINT             CashedStringNumber;
	UINT             TriangleCount = 0;

	Textures.reserve(3);
	FILE *file = fopen(FileName,"rt");

	if (file != NULL)
	{
		while (fgets(buffer,FILE_STRING_MAX_LENGHT,file))
		{
			buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';
			if ((strcmp(buffer,"triangles") == 0) && (0 == TriangleCount))
				while (fgets(buffer, FILE_STRING_MAX_LENGHT, file))
				{
					buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';
					if(strcmp(buffer,"end") == 0)
					{
						TempVertex = new STANDART_VERTEX[TriangleCount*3];
						rewind(file);
						break;
					}

					fgets(buffer,1023,file);
					fgets(buffer,1023,file);
					fgets(buffer,1023,file);
					TriangleCount++;
				}
			//--------------------------------------
			if ( (strcmp(buffer,"triangles") == 0) && ( TempVertex != NULL ) )
			{
				for (UINT i = 0; i < TriangleCount ;i++)
				{
					fgets( buffer, FILE_STRING_MAX_LENGHT, file );
					buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';

					if ( strcmp( buffer, TextureNameCash.c_str() ) != 0 )
					{
						bool TextureNameFound = false;

						for ( UINT i = 0; i < Textures.size(); i++ )
						{
							if ( strcmp( buffer, Textures[i].name.c_str() ) == 0 )
							{
								TextureNameCash = Textures[i].name;
								CashedStringNumber = i;
								TextureNameFound = true;
								break;
							}
						}

						if ( !TextureNameFound )
						{
							Textures.push_back( TEXTURE_DESCRIPTOR( std::string( buffer ), (short)Textures.size() ) );
							TextureNameCash.assign( buffer );
							CashedStringNumber = Textures.size() - 1;
						}
					}

					for ( int j = 0; j < 3; j++ )
					{
						fgets( buffer, FILE_STRING_MAX_LENGHT, file );
						ParseVertex( buffer, &TempVertex[i*3+j] );
						TempVertex[i*3+j].bonesIndexesAndTextureNumber.w = CashedStringNumber;
					}
				}

				Result = CompressBuffersToModel( TempVertex, TriangleCount * 3 );
				Result->textures = Textures;
			}
		}
	}
	else
	{
		fprintf( stderr, "ERROR!! File %s not found...\n", FileName );
	}

	if ( NULL != TempVertex )
	{
	    delete[] TempVertex;
	}

	fclose(file);
	file = NULL;
	return Result;
}

void
CResourceLoader::CalculateGabarits( const SmdModel& Model,
									float &DimensionSphereRadius,
									glm::vec3 &DimensionSphereCenter,
									glm::vec3 &DimentionCorner )
{
	 // minimal corner
	glm::vec3 tmpOutFirstCorner = glm::vec3( Model.vertices[0].position.x,
											 Model.vertices[0].position.y,
											 Model.vertices[0].position.z );
	// maximal corner
	glm::vec3 tmpOutSecondCorner =  glm::vec3( tmpOutFirstCorner );

	//Find dimensions
	for ( UINT i = 1; i < Model.vertexCount; i++ )
	{
		STANDART_VERTEX WorkVertex = Model.vertices[i];
		if ( WorkVertex.position.x > tmpOutSecondCorner.x )
		{
			tmpOutSecondCorner.x = WorkVertex.position.x;
		}
		else if ( WorkVertex.position.x < tmpOutFirstCorner.x )
		{
			tmpOutFirstCorner.x = WorkVertex.position.x;
		}

		if ( WorkVertex.position.y > tmpOutSecondCorner.y )
		{
			tmpOutSecondCorner.y = WorkVertex.position.y;
		}
		else if ( WorkVertex.position.y < tmpOutFirstCorner.y )
		{
			tmpOutFirstCorner.y = WorkVertex.position.y;
		}

		if ( WorkVertex.position.z > tmpOutSecondCorner.z )
		{
			tmpOutSecondCorner.z = WorkVertex.position.z;
		}
		else if ( WorkVertex.position.z < tmpOutFirstCorner.z )
		{
			tmpOutFirstCorner.z = WorkVertex.position.z;
		}
	}

	DimentionCorner = tmpOutSecondCorner;

	DimensionSphereCenter = ( tmpOutSecondCorner + tmpOutFirstCorner ) / 2.0f;
	DimensionSphereRadius = glm::length( tmpOutSecondCorner - tmpOutFirstCorner ) / 2.0f;
}

void
CResourceLoader::ReadTexturesFromFile( FILE *file, vector<string> &TextureNames, vector< pair<GLuint,GLenum> > &TextureIDs )
{
	char buffer[FILE_STRING_MAX_LENGHT] = "";
	bool ReadTextureArray = false;
	//Uses for texture arrays only
	vector<string> TexturePaths;

	TextureNames.reserve(5);
	TextureIDs.reserve(5);

	while ( ( strcmp( buffer, "end" ) != 0 ) && fgets( buffer, FILE_STRING_MAX_LENGHT, file) )
	{
		buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';
		if ( strcmp( buffer, "{" ) == 0 )
		{
			//To detect syntax errors
			assert( false == ReadTextureArray );
			ReadTextureArray = true;
			TexturePaths.clear();
			TexturePaths.reserve(5);

			fgets( buffer, FILE_STRING_MAX_LENGHT, file);
			buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';

			//Write internal engine name of array
			TextureNames.push_back( string(buffer) );
		}
		else if (  strcmp( buffer, "}" ) == 0 )
		{
			GLuint NewTextureID = SetUpTexture2D( TexturePaths );
			TextureIDs.push_back( pair<GLuint,GLenum>( NewTextureID, GL_TEXTURE_2D_ARRAY ) );
			ReadTextureArray = false;
		}
		else
		{
			char TextureName[FILE_TOKEN_MAX_LENGHT];
			char TexturePath[FILE_TOKEN_MAX_LENGHT];
			sscanf( buffer, "%s %s", TextureName, TexturePath );
			if ( ReadTextureArray )
			{
				TexturePaths.push_back( string(TexturePath) );
			}
			else
			{
				TextureNames.push_back( string(TextureName) );
				GLuint NewTextureID = SetUpTexture2D( TexturePath );
				TextureIDs.push_back( pair<GLuint,GLenum>( NewTextureID, GL_TEXTURE_2D ) );
			}
		}
	}
}

RENDER_MODEL_INFO*
CResourceLoader::LoadModelFromFileAndRegister(  const char* ModelConfig,
												string &ModelName,
		  	  	  	  	  	  	  	  	  	    float &DimensionSphereRadius,
		  	  	  	  	  	  	  	  	  	    glm::vec3 &DimensionSphereCenter,
		  	  	  	  	  	  	  	  	  	    glm::vec3 &DimensionCorner  )
{
	RENDER_MODEL_INFO* NewModelRenderInfo = NULL;

	char ObjectName[FILE_TOKEN_MAX_LENGHT];
	char ObjectPath[FILE_TOKEN_MAX_LENGHT];

	sscanf( ModelConfig, "%s %s", ObjectName, ObjectPath );

	ModelName.append( ObjectName );
	SmdModel* NewSmdModel = LoadSMDModel( ObjectPath );

	CalculateGabarits( (*NewSmdModel), DimensionSphereRadius, DimensionSphereCenter, DimensionCorner );
	NewModelRenderInfo = new RENDER_MODEL_INFO( NewSmdModel->vertexCount,
												std::vector<GLushort> ( NewSmdModel->indexes,
																		NewSmdModel->indexes +
																		( NewSmdModel->triangleCount * 3 ) ),
												NewSmdModel->textures );
	//Prepare model to register in OpenGL
	PURE_COMPRESSED_MODEL *CompressedModel =
				   new PURE_COMPRESSED_MODEL ( std::vector<STANDART_VERTEX> ( NewSmdModel->vertices, NewSmdModel->vertices +
				                                                              ( NewSmdModel->vertexCount ) ),
											   std::vector<GLushort> ( NewSmdModel->indexes,
													                   NewSmdModel->indexes +
													                    ( NewSmdModel->triangleCount * 3 ) ) );
	CompressedModel->renderInfo = NewModelRenderInfo;
	compressedModelsAray.push_back( CompressedModel );

	return NewModelRenderInfo;
}

CResourceLoader::SubobjectConstructionInfo*
CResourceLoader::LoadSubobjectFromFile( FILE *file )
{
	char buffer[FILE_STRING_MAX_LENGHT];
	SubobjectConstructionInfo* NewSubobject = new SubobjectConstructionInfo();

	if ( fgets( buffer, FILE_STRING_MAX_LENGHT, file) )
	{
		buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';

		//Load object name and model
		char FirstToken[FILE_TOKEN_MAX_LENGHT];
		char SecondToken[FILE_TOKEN_MAX_LENGHT];
		sscanf( buffer, "%s %s", FirstToken, SecondToken );

		NewSubobject->name.append( FirstToken );
		NewSubobject->modelName.append( SecondToken );

		fgets( buffer, FILE_STRING_MAX_LENGHT, file);
		buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';

		while ( strcmp( buffer, "Textureend" ) != 0 )
		{
			sscanf( buffer, "%s %s", FirstToken, SecondToken );

			NewSubobject->textures.push_back( pair<string,string>( string(FirstToken),
					                                               string(SecondToken) ) );

			if ( !fgets( buffer, FILE_STRING_MAX_LENGHT, file) )
			{
				//Exception
			}
			buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';
		}
		fgets( buffer, FILE_STRING_MAX_LENGHT, file);
		buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';

		while ( strcmp( buffer, "Animend" ) != 0 )
		{
			sscanf( buffer, "%s %s", FirstToken, SecondToken );

			NewSubobject->animations.push_back( pair<string,string>( string(FirstToken),
					                                               string(SecondToken) ) );

			if ( !fgets( buffer, FILE_STRING_MAX_LENGHT, file) )
			{
				//Exception
			}
			buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';
		}

		//read "end" marker
		fgets( buffer, FILE_STRING_MAX_LENGHT, file);
	}

	return NewSubobject;
}

CResourceLoader::UnitTypeInfo*
CResourceLoader::LoadUnitTypesFromFile( FILE *file )
{
	UnitTypeInfo* NewUnitType = new UnitTypeInfo();

	char buffer[FILE_STRING_MAX_LENGHT];
	char FirstToken[FILE_TOKEN_MAX_LENGHT];
	char SecondToken[FILE_TOKEN_MAX_LENGHT];

	fgets( buffer, FILE_STRING_MAX_LENGHT, file);
	buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';

	sscanf( buffer, "%s %s", FirstToken, SecondToken );
	NewUnitType->name.append( FirstToken );

	if ( strcmp( SecondToken, TO_STRING( UNIT_TYPE_HEAVY_JET ) ) == 0 )
	{
		NewUnitType->unitType = UNIT_TYPE_HEAVY_JET;
	}
	else if ( strcmp( SecondToken, TO_STRING( UNIT_TYPE_MEDIUM_JET ) ) == 0 )
	{
		NewUnitType->unitType = UNIT_TYPE_MEDIUM_JET;
	}
	else if ( strcmp( SecondToken, TO_STRING( UNIT_TYPE_LIGHT_JET ) ) == 0 )
	{
		NewUnitType->unitType = UNIT_TYPE_LIGHT_JET;
	}
	else if ( strcmp( SecondToken, TO_STRING( UNIT_TYPE_RACKET ) ) == 0 )
	{
		NewUnitType->unitType = UNIT_TYPE_RACKET;
	}
	else if ( strcmp( SecondToken, TO_STRING( UNIT_TYPE_MILK ) ) == 0 )
	{
		NewUnitType->unitType = UNIT_TYPE_MILK;
	}
	else if ( strcmp( SecondToken, TO_STRING( UNIT_TYPE_BULLET ) ) == 0 )
	{
		NewUnitType->unitType = UNIT_TYPE_BULLET;
	}
	else if ( strcmp( SecondToken, TO_STRING( UNIT_TYPE_BONUS ) ) == 0 )
	{
		NewUnitType->unitType = UNIT_TYPE_BONUS;
	}
	else if ( strcmp( SecondToken, TO_STRING( UNIT_TYPE_SCENARY ) ) == 0 )
	{
		NewUnitType->unitType = UNIT_TYPE_SCENARY;
	}
	else if ( strcmp( SecondToken, TO_STRING( UNIT_TYPES_TOTAL_COUNT ) ) == 0 )
	{
		NewUnitType->unitType = UNIT_TYPES_TOTAL_COUNT;
	}
	else
	{
		return NULL;
	}

	CResourceWarehouse* Warehouse = ResourceOwner::GetResourceWarehouse();
	while( fgets( buffer, FILE_STRING_MAX_LENGHT, file) )
	{
		buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';
		if ( strcmp( buffer, "Unitend" ) == 0 )
		{
			break;
		}

		UnitTypeInfo::SubobjectConfiguration NewSubobject;

		sscanf( buffer, "%s %f %f %f %f %f %f %f %f %f %s",
				FirstToken,
				&NewSubobject.relativePosition.x,
				&NewSubobject.relativePosition.y,
				&NewSubobject.relativePosition.z,
				&NewSubobject.relativeOrientation.x,
				&NewSubobject.relativeOrientation.y,
				&NewSubobject.relativeOrientation.z,
				&NewSubobject.dimesionSizes.x,
				&NewSubobject.dimesionSizes.y,
				&NewSubobject.dimesionSizes.z,
				SecondToken );

		//Get shader constructor
		std::string ShaderTypeName( SecondToken );
		CShaderConstructor *ShaderConstructor = Warehouse->GetShaderProgramConstructor( ShaderTypeName );
		if ( NULL == ShaderConstructor )
		{
			fprintf( stderr, "Shader type %s not found!\n", ShaderTypeName.c_str() );
			exit(0);
		}

		//Read Unit-specific shader input parameters
		std::string ShaderInputParameters;
		while( fgets( buffer, FILE_STRING_MAX_LENGHT, file) )
		{
			buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';
			if ( strcmp( buffer, "end" ) == 0 )
			{
				break;
			}
			ShaderInputParameters += std::string( buffer );
		}

		NewSubobject.shaderProgram = ShaderConstructor->ConstructShader( ShaderInputParameters );
		assert( NewSubobject.shaderProgram );
		NewSubobject.subobjectName.append( FirstToken );

		NewUnitType->subobjects.push_back( NewSubobject );
	}

	return NewUnitType;
}

void
CResourceLoader::SetupGuiDummy( GAME_SUBOBJECT_INSTANCE *GuiSubobject )
{
	CResourceWarehouse* Warehouse = ResourceOwner::GetResourceWarehouse();

	CShaderConstructor *ShaderConstructor = Warehouse->GetShaderProgramConstructor( GUI_SHADER_PROGRAM );
	GuiSubobject->SetShaderProgram( ShaderConstructor->ConstructShader( "" ) );

	SUBOBJECT_WITH_CONTROLLER *SubobjectWithController = new SUBOBJECT_WITH_CONTROLLER( GuiSubobject, NULL );
	GAME_OBJECT* GuiGameObject = new GAME_OBJECT();

	GuiGameObject->models.reserve( 1 );
	GuiGameObject->models.push_back( SubobjectWithController );

	Warehouse->SetGuiDummy( GuiGameObject );
}

void
CResourceLoader::SetupSubobjectParameters( SUBOBJECT_WITH_CONTROLLER &SubobjectWithController,
										   const SubobjectConstructionInfo &CustomInfo )
{
	CResourceWarehouse* Warehouse = ResourceOwner::GetResourceWarehouse();
	GAME_SUBOBJECT_INSTANCE *Subobject = SubobjectWithController.GetSubobject();
	//CAnimationController* AnimationController = SubobjectWithController.GetAnimationController();

	for ( vector< pair<string,string> >::const_iterator Begin = CustomInfo.textures.begin(),
		  End = CustomInfo.textures.end();
		  Begin != End;
		  Begin++ )
	{
		TEXTURE_DESCRIPTOR *Texture = Warehouse->GetTextureDescriptorByName( (*Begin).second );
		assert( Texture );
		Texture->alias = (*Begin).first;
		Subobject->UpdateTexture( *Texture );
	}
}

std::pair<GLuint, std::vector<std::string> >
CResourceLoader::GetShaderName( const std::string ShaderPath, boost::unordered_map< string, std::pair<GLuint, std::vector<std::string> > > &LoadedShaders )
{
	boost::unordered_map< string, std::pair<GLuint, std::vector<std::string> > >::const_iterator FoundElement = LoadedShaders.find( ShaderPath );

	std::pair<GLuint, std::vector<std::string> > LoadedShaderName;
	if ( LoadedShaders.end() == FoundElement )
	{
		CVisualisator* Visualisator = CVisualisator::GetVisualisator();
		LoadedShaderName = Visualisator->SetUpShader( ShaderPath );
		LoadedShaders.emplace( ShaderPath, LoadedShaderName );
	}
	else
	{
		LoadedShaderName = FoundElement->second;
	}

	return LoadedShaderName;
}

CGameWorld::CPhysicsParam*
CResourceLoader::LoadGameWorldPhysics( FILE *File )
{
	CGameWorld::CPhysicsParam *Result = new CGameWorld::CPhysicsParam();
	return Result;
}

CGameWorld::CCameraParam*
CResourceLoader::LoadGameWorldCamera( FILE *File )
{
	CGameWorld::CCameraParam *Result = new CGameWorld::CCameraParam();
	return Result;
}

std::vector<CGameWorld::CGameObjectParam* >
CResourceLoader::LoadGameWorldObjects( FILE *File )
{
	std::vector<CGameWorld::CGameObjectParam* > Result;
	char buffer[FILE_STRING_MAX_LENGHT];

	//get result vector size
	UINT ObjectCounter = 0;
	while( fgets( buffer, FILE_STRING_MAX_LENGHT, File ) )
	{
		buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';
		if ( strcmp( buffer, "Object" ) == 0 )
		{
			ObjectCounter++;
		}
	}
	Result.reserve( ObjectCounter );

	rewind( File );
	while( fgets( buffer, FILE_STRING_MAX_LENGHT, File ) )
	{
		buffer[strlen(buffer) - OFFSET_FROM_LINE_END] = '\0';

		if ( strcmp( buffer, "Object" ) == 0 )
		{
			char ObjectType[FILE_TOKEN_MAX_LENGHT];
			char ObjectName[FILE_TOKEN_MAX_LENGHT];
			glm::vec3 Position;
			glm::vec3 Orientation;
			float Speed = 0;

			// Skip '{'
			fgets( buffer, FILE_STRING_MAX_LENGHT, File );
			// Get names
			fgets( buffer, FILE_STRING_MAX_LENGHT, File );
			sscanf( buffer, "%s %s", ObjectType, ObjectName );
			// Get position
			fgets( buffer, FILE_STRING_MAX_LENGHT, File );
			sscanf( buffer, "%f %f %f", &Position.x, &Position.y, &Position.z );
			// Get orientation
			fgets( buffer, FILE_STRING_MAX_LENGHT, File );
			sscanf( buffer, "%f %f %f", &Orientation.x, &Orientation.y, &Orientation.z );
			// Get speed
			fgets( buffer, FILE_STRING_MAX_LENGHT, File );
			sscanf( buffer, "%f", &Speed );
			// Skip '}'
			fgets( buffer, FILE_STRING_MAX_LENGHT, File );

			CGameWorld::CGameObjectParam* NewObject = new CGameWorld::CGameObjectParam( ObjectType,
																						ObjectName,
																						Position,
																						Orientation,
																						Speed );
			Result.push_back( NewObject );
		}
	}

	return Result;
}

bool
CResourceLoader::LoadKeyboardConfigFromFile( FILE *File, IGameLogic* GameLogic )
{
	char Buffer[FILE_STRING_MAX_LENGHT];
	char Token[FILE_TOKEN_MAX_LENGHT];
	char Character = 0;
	UINT KeyIndex, KeyNewValue;
	bool Result = true;

	std::vector< std::pair<UINT,SDLKey> > KeyboarbMap;
	KeyboarbMap.reserve( SDLK_LAST );

	while( fgets( Buffer, FILE_STRING_MAX_LENGHT, File ) )
	{
		sscanf( Buffer, "%s %c %u %c %u", Token, &Character, &KeyIndex, &Character, &KeyNewValue );

		KeyboarbMap[KeyIndex] = static_cast<SDLKey>( KeyNewValue );
	}

	GameLogic->LoadDataTable<UINT,SDLKey>( KeyboarbMap );

	return Result;
}
